use std::str::FromStr;

use crate::{
    models::assets::AssetsModelDTO,
    services::assets_service::{AssetsService, DeleteAssetsReq, FindAllAssetsReq},
    AppResult, PaginationQuery,
};
use actix_multipart::Multipart;
use actix_web::{web, HttpResponse};
use serde::{Deserialize, Serialize};
use wither::bson::oid::ObjectId;
#[derive(Debug, Serialize, Deserialize)]
pub struct UploadFileCtrReq {
    pub o_id: Option<String>,
}

mod serde_default {
    pub fn draw_count() -> u64 {
        10
    }
}

async fn upload_file(
    assets_service: web::Data<AssetsService>,
    multipart: Multipart,
    req: web::Query<UploadFileCtrReq>,
) -> AppResult<HttpResponse> {
    let req = req.into_inner();
    let res = assets_service
        .upload_assets(
            multipart,
            req.o_id
                .as_deref()
                .map(ObjectId::from_str)
                .and_then(Result::ok),
        )
        .await?
        .iter()
        .map(|model| AssetsModelDTO::from(model.clone()))
        .collect::<Vec<_>>();
    Ok(HttpResponse::Ok().json(res))
}

async fn find_all_assets(
    assets_service: web::Data<AssetsService>,
    query: web::Query<PaginationQuery>,
    req: web::Query<FindAllAssetsReq>,
) -> AppResult<HttpResponse> {
    let res = assets_service
        .find_all_assets(&query.into_inner(), req.into_inner())
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<AssetsModelDTO>()))
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteAssetsCtrlPath {
    pub assets_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteAssetsCtrlQuery {
    #[serde(default = "Default::default", rename = "f")]
    pub force: bool,
}

async fn delete_assets(
    assets_service: web::Data<AssetsService>,
    path_req: web::Path<DeleteAssetsCtrlPath>,
    query_req: web::Query<DeleteAssetsCtrlQuery>,
) -> AppResult<HttpResponse> {
    let assets_id = path_req.into_inner().assets_id;
    let force = query_req.force;
    assets_service
        .delete_assets(&DeleteAssetsReq { assets_id, force })
        .await?;
    Ok(HttpResponse::NoContent().finish())
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DrawAssetsCtrlQuery {
    #[serde(default = "serde_default::draw_count")]
    pub count: u64,
}
async fn draw_assets(
    assets_service: web::Data<AssetsService>,
    query_req: web::Query<DrawAssetsCtrlQuery>,
) -> AppResult<HttpResponse> {
    let res = assets_service
        .draw_assets(query_req.count)
        .await?
        .iter()
        .map(|v| AssetsModelDTO::from(v.clone()))
        .collect::<Vec<_>>();
    Ok(HttpResponse::Ok().json(res))
}

pub fn configure_assets_controller(config: &mut web::ServiceConfig) {
    config
        .service(
            web::resource("")
                .route(web::post().to(upload_file))
                .route(web::get().to(find_all_assets)),
        )
        .service(web::resource("/single/{assets_id}").route(web::delete().to(delete_assets)))
        .service(web::resource("/draw").route(web::get().to(draw_assets)));
}
